package geometrie;

/**
 * 
 * 
 *
 */
public class Transformation {
	private double[][] transformationMatrix;

	double[][] getMatrix() {
		return this.transformationMatrix;
	}

	/**
	 * Constructor of transformation
	 * 
	 * @param transfMatrix
	 *            the transformation matrix in homogeneous coordinates
	 */
	public Transformation(double[][] transfMatrix) {
		this.transformationMatrix = transfMatrix;
	}

	/**
	 * constructor used for the static method translation
	 * 
	 * @param tx
	 *            the deplacement in the x coordinate
	 * @param ty
	 *            the deplacement in the y coordinate
	 * @param tz
	 *            the deplacement in the z coordinate
	 */

	private Transformation(double tx, double ty, double tz) {
		double[][] transfMatrix = { { 1, 0, 0, tx }, { 0, 1, 0, ty },
				{ 0, 0, 1, tz }, { 0, 0, 0, 1 } };
		transformationMatrix = transfMatrix;
	}

	/**
	 * constructor for the identite static method
	 */
	private Transformation() {
		double[][] transfMatrix = { { 1, 0, 0, 0 }, { 0, 1, 0, 0 },
				{ 0, 0, 1, 0 }, { 0, 0, 0, 1 } };
		transformationMatrix = transfMatrix;
	}

	/**
	 * constructor used for the compose static method.
	 * 
	 * @param tr1
	 *            the first transformation
	 * @param tr2
	 *            the second transformation the composition is done by
	 *            multiplying the transformation matrix of tr1 and tr2 in this
	 *            order.
	 */
	private Transformation(Transformation tr1, Transformation tr2) {
		transformationMatrix = new double[4][4];
		double[][] M1 = tr1.transformationMatrix;
		double[][] M2 = tr2.transformationMatrix;
		transformationMatrix = matrixMultiplication(M1, M2, 4, 4, 4, 4);
	}

	/**
	 * Constructor used for the rotation static method
	 * 
	 * @param axe
	 *            the axe to rotate around (x, y, z)
	 * @param angle
	 *            the angle to rotate (IN RADIANS)
	 */
	private Transformation(Point axe, double angle) {
		transformationMatrix = new double[4][4];
		// angle = (Math.PI/180)*angle;
		double c = Math.cos(angle);
		double s = Math.sin(angle);
		double k = 1 - c;
		double x, y, z;
		x = axe.getXCartesien();
		y = axe.getYCartesien();
		z = axe.getZCartesien();
		this.transformationMatrix[0][0] = k * x * x + c;
		this.transformationMatrix[0][1] = k * y * z - s * z;
		this.transformationMatrix[0][2] = k * z * x + s * y;
		this.transformationMatrix[0][3] = 0;
		this.transformationMatrix[1][0] = k * x * y + s * z;
		this.transformationMatrix[1][1] = k * y * y + c;
		this.transformationMatrix[1][2] = k * z * y - s * x;
		this.transformationMatrix[1][3] = 0;
		this.transformationMatrix[2][0] = k * x * z - s * y;
		this.transformationMatrix[2][1] = k * y * z + s * x;
		this.transformationMatrix[2][2] = k * z * z + c;
		this.transformationMatrix[2][3] = 0;
		this.transformationMatrix[3][0] = 0;
		this.transformationMatrix[3][1] = 0;
		this.transformationMatrix[3][2] = 0;
		this.transformationMatrix[3][3] = 1;
	}

	/**
	 * Method that creates a Transformation translation with the given
	 * translation in x, y and z
	 * 
	 * @param tx
	 *            the translation in x
	 * @param ty
	 *            the translation in y
	 * @param tz
	 *            the translation in z
	 * @return the transformation
	 */
	public static Transformation translation(double tx, double ty, double tz) {
		Transformation translation = new Transformation(tx, ty, tz);
		return translation;
	}

	/**
	 * Method that creates a identity transformation
	 * 
	 * @return the transformation
	 */
	public static Transformation identite() {
		Transformation identite = new Transformation();
		return identite;
	}

	/**
	 * Method that composes to transformations
	 * 
	 * @param tr1
	 *            the first transformation
	 * @param tr2
	 *            the second transformation
	 * @return the new transformation made by composing tr1 and tr2 in these
	 *         order
	 */
	public static Transformation compose(Transformation tr1, Transformation tr2) {
		Transformation compose = new Transformation(tr1, tr2);
		return compose;
	}

	/**
	 * Method that create a rotation transformation
	 * 
	 * @param axe
	 *            the vector/axe in which the rotation will be done around
	 * @param angle
	 *            the angle of rotation(in radians)
	 * @return the transformation
	 */

	public static Transformation rotation(Point axe, double angle) {
		Transformation rotation = new Transformation(axe, angle);
		return rotation;
	}

	/**
	 * Method to multiply to matrix
	 * 
	 * @param A
	 *            the first matrix
	 * @param B
	 *            the second matrix
	 * @param row1
	 *            the number of rows in the first matrix
	 * @param col1
	 *            the number of columns in the first matrix
	 * @param row2
	 *            the number of rows in the second matrix
	 * @param col2
	 *            the number of columns in the second matrix
	 * @return the new matrix result of the multiplication of A and B. If the
	 *         matrices have wrong size (col(M1) != row(M2)) the method returns
	 *         null.
	 */
	static double[][] matrixMultiplication(double[][] A, double[][] B,
			int row1, int col1, int row2, int col2) {
		int i, j, k;

		if (col1 != row2) {
			return null;
		}

		double[][] C = new double[row1][col2];
		for (i = 0; i < row1; i++) {
			for (j = 0; j < col2; j++) {
				C[i][j] = 0;
				for (k = 0; k < col1; k++) {
					C[i][j] += A[i][k] * B[k][j];
				}
			}
		}
		return C;
	}

	public String toString() {
		int i, j;
		String newString = "";
		for (i = 0; i < 4; i++) {
			for (j = 0; j < 4; j++) {
				newString += this.getMatrix()[i][j] + " ";
			}
			newString += "\n";
		}
		return newString;
	}
}
